/*
 * Copyright 2008, Freescale Semiconductor, Inc
 * Andy Fleming
 *
 * Based vaguely on the Linux code
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <config.h>
#include <common.h>
#include <command.h>
#include <mmc.h>
#include <part.h>
#include <malloc.h>
#include <linux/list.h>
#include <div64.h>
#include <oem_func.h>
#include "comman.h"

static const int fbase[] = {
10000,
100000,
1000000,
10000000,
};

/* Multiplier values for TRAN_SPEED.  Multiplied by 10 to be nice
* to platforms without floating point.
*/
static const int multipliers[] = {
0,	/* reserved */
10,12,13,15,20,
25,30,35,40,45,
50,55,60,70,80,
};
int mmc_send_op_cond(struct mmc *mmc)
{
	int timeout = 1000;                                                                  
    int err;                                                                             
    struct mmc_cmd cmd; 
	mmc_go_idle(mmc);                                                                    
     /*                                                                                
	cmd.cmdidx = MMC_CMD_SEND_OP_COND;     
	cmd.resp_type = MMC_RSP_R3;            
	cmd.cmdarg = 0;                        
	cmd.flags = 0;                         
		                                       
	err = mmc_send_cmd(mmc, &cmd, NULL);   
		                                       
	if (err)                               
		return err;                        
*/
	do {                                                                                 
		cmd.cmdidx = MMC_CMD_SEND_OP_COND;                                           
		cmd.resp_type = MMC_RSP_R3;                                                  
	//	cmd.cmdarg = (mmc->voltages &(cmd.response[0] & OCR_VOLTAGE_MASK)) |
	//		(cmd.response[0] & OCR_ACCESS_MODE);  
		cmd.cmdarg = OCR_HCS | mmc->voltages; 
		cmd.flags = 0;                                                               
		err = mmc_send_cmd(mmc, &cmd, NULL);                                         

		if (err)                                                                     
			return err;                                                         

		udelay(1000);                                                                
	} while (!(cmd.response[0] & OCR_BUSY) && timeout--);                                
                                                                                      
	if (timeout <= 0)                                                                   
		return UNUSABLE_ERR;                                                         
                                                                                      
	mmc->version = MMC_VERSION_UNKNOWN;                                                  
	mmc->ocr = cmd.response[0];                                                          
  	  
	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);                          
	mmc->rca = 0; 
	return 0;
}
int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
{
	struct mmc_cmd cmd;

	cmd.cmdidx = MMC_CMD_SWITCH;
	cmd.resp_type = MMC_RSP_R1b;
	cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
		(index << 16) |
		(value << 8);
	cmd.flags = 0;

	return mmc_send_cmd(mmc, &cmd, NULL);
}

int mmc_change_freq(struct mmc *mmc)
{
	char ext_csd[512];
	char cardtype;
	int err;

	mmc->card_caps = 0;

	/* Only version 4 supports high-speed */
	if (mmc->version < MMC_VERSION_4){
		return 0;
	}

	mmc->card_caps |= MMC_MODE_4BIT;

	err = mmc_send_ext_csd(mmc, ext_csd);

	if (err){
		return err;
	}
	cardtype = ext_csd[196] & 0xf;

	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);

	if (err){
		return err;
	}

	/* Now check to see that it worked */
	err = mmc_send_ext_csd(mmc, ext_csd);

	if (err){
		return err;
	}

	/* No high-speed support */
	if (!ext_csd[185])
		return 0;

	/* High Speed is set, there are two types: 52MHz and 26MHz */
//	if (cardtype & MMC_HS_52MHZ&&(0==mmc->Wflag)){
	if (cardtype & MMC_HS_52MHZ){
		mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
	}else{
		mmc->card_caps |= MMC_MODE_HS;
	}

	return 0;
}

static int mmc_decode_csd(struct mmc *mmc)
{
	 struct mmc_csd *csd = &mmc->csd_data;
         unsigned int e, m, csd_struct; 
         u32 *resp = mmc->csd;     

         /*
        * We only understand CSD structure v1.1 and v1.2.
        * v1.2 has extra information in bits 15, 11 and 10.
        */               
         csd_struct = UNSTUFF_BITS(resp, 126, 2);
         if (csd_struct != 1 && csd_struct != 2 && csd_struct != 3) {
                 printf("%s: unrecognised CSD structure version %d\n",
                        mmc->name, csd_struct);
                 return -1;                
         }
    
         csd->mmca_vsn    = UNSTUFF_BITS(resp, 122, 4);  
	 mmc->version=csd->mmca_vsn; 
//	 printf("MMC Card %s Version:%d\n",mmc->name,csd->mmca_vsn);
    //     m = UNSTUFF_BITS(resp, 115, 4);
      //   e = UNSTUFF_BITS(resp, 112, 3);
        // csd->tacc_ns     = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
        // csd->tacc_clks   = UNSTUFF_BITS(resp, 104, 8) * 100;
//	 printf("mmc tacc_ns:%d\n",csd->tacc_ns); 
         m = UNSTUFF_BITS(resp, 99, 4); 
         e = UNSTUFF_BITS(resp, 96, 3); 
         csd->max_dtr      = mmc_calculate_dtr( m,e);//tran_exp[e] * tran_mant[m];  
//	 printf("csd->max_dtr:%2d\n",csd->max_dtr);
	 mmc->tran_speed = fbase[e]*multipliers[m];
//	 printf("mmc->tran_speed:%2d\n",mmc->tran_speed);
         csd->cmdclass     = UNSTUFF_BITS(resp, 84, 12);  
         e = UNSTUFF_BITS(resp, 47, 3); 
         m = UNSTUFF_BITS(resp, 62, 12);
	 if(m==0xFFF){
		 mmc->sector_count=0;
	 }else{
         	mmc->sector_count  = (1 + m) << (e + 2);
		printf("%x sectors\n",mmc->sector_count);
	 }
	
         mmc->read_bl_len =1<< UNSTUFF_BITS(resp, 80, 4);
 //        csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
   //      csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
     //    csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
        // csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
         mmc->write_bl_len =1<< UNSTUFF_BITS(resp, 22, 4);
      //   csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
	
	switch ( mmc->version) {
		case 0:
			mmc->version = MMC_VERSION_1_2;
			break;
		case 1:
			mmc->version = MMC_VERSION_1_4;
			break;
		case 2:
			mmc->version = MMC_VERSION_2_2;
			break;
		case 3:
			mmc->version = MMC_VERSION_3;
			break;
		case 4:
			mmc->version = MMC_VERSION_4;
			break;
		default:
			mmc->version = MMC_VERSION_1_2;
			break;
	}
	
  	if (mmc->read_bl_len > 512)                                                           
             mmc->read_bl_len = 512;                                                       
                                                                                         
       	if (mmc->write_bl_len > 512)                                                          
             mmc->write_bl_len = 512;       
         
         return 0;

}
static int mmc_set_relative_addr(struct mmc *mmc)
{
      struct mmc_cmd cmd;

      cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;                                             
      cmd.cmdarg = mmc->rca << 16;                                                        
      cmd.resp_type = MMC_RSP_R6;                                                         
      cmd.flags = 0;                                                                      
                                                                                   
      return mmc_send_cmd(mmc, &cmd, NULL); 
}
int mmc_startup(struct mmc *mmc)
{
	int err;
	DEBUG_MMC("0");
	/* Put the Card in Identify Mode */
	if(mmc_send_cid(mmc)) return -1;
	/*
	 * For MMC cards, set the Relative Address.
	 * This also puts the cards into Standby State
	 */
	DEBUG_MMC("1");
	if(mmc_set_relative_addr(mmc))return -1;
	/* Get the Card-Specific Data */
	DEBUG_MMC("2");
	if(block_send_csd(mmc)) return -1;
	mmc_decode_csd(mmc);
	DEBUG_MMC("3");
	/* Select the card, and put it into Transfer Mode */
	
	if(mmc_into_trans_mode(mmc)) return -1;

	err = mmc_change_freq(mmc);
	if (err)
		return err;

	/* Restrict card's capabilities by what the host can do */
	mmc->card_caps |= mmc->host_caps;
		if (mmc->card_caps & MMC_MODE_4BIT) {
			/* Set the card to use 4 bit*/
			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
					EXT_CSD_BUS_WIDTH,
					EXT_CSD_BUS_WIDTH_4);

			if (err)
				return err;

			mmc_set_bus_width(mmc, 4);
		} else if (mmc->card_caps & MMC_MODE_8BIT) {
			/* Set the card to use 8 bit*/
			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
					EXT_CSD_BUS_WIDTH,
					EXT_CSD_BUS_WIDTH_8);

			if (err)
				return err;
			mmc_set_bus_width(mmc, 8);
		}
#ifdef CONFIG_MMC_RECOMMAND_FREQ
		mmc_set_clock(mmc, CONFIG_MMC_RECOMMAND_FREQ);
#else
		if (mmc->card_caps & MMC_MODE_HS) {
			if (mmc->card_caps & MMC_MODE_HS_52MHz){
				mmc_set_clock(mmc, 52000000);
				printf("Set clock 52MHz\n");
			}else{
				mmc_set_clock(mmc, 26000000);
				printf("Set clock 26MHz\n");
			}
		} else
			mmc_set_clock(mmc, 20000000);
#endif
	/* fill in device description */
	mmc_init_devdesc(mmc);

	mmc_set_blocklen(mmc, mmc->read_bl_len); 

	if(0==mmc->sector_count){
		char ext_csd[512];
		if(mmc_send_ext_csd(mmc,ext_csd)) return -1;
	 	mmc->sector_count=ext_csd[212];                                      
         	mmc->sector_count|=ext_csd[213]<<8;                                  
         	mmc->sector_count|=ext_csd[214]<<16;                                 
         	mmc->sector_count|=ext_csd[215]<<24;
		if(!mmc->sector_count) return -1;
	}
	mmc->initflag=0x5E;
	return 0;
}

